package com.black.utils.proxy.handler;

import com.black.utils.proxy.handler.debug.ProcessTimeChainHandler;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

/**
 * @author ylx
 */
public abstract class AbstractChainHandler implements MethodInterceptor {
    protected final Class<? extends Annotation> targetAnnotation;

    protected AbstractChainHandler next;

    public AbstractChainHandler(Class<? extends Annotation> targetAnnotation) {
        this.targetAnnotation = targetAnnotation;
    }

    @Override
    public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        if (method.isAnnotationPresent(targetAnnotation)) {
            before(object, method, args, methodProxy);
        }
        Object result;
        if (next != null) {
            result = next.intercept(object, method, args, methodProxy);
        } else {
            result = methodProxy.invokeSuper(object, args);
        }
        if (method.isAnnotationPresent(targetAnnotation)) {
            after(object, method, args, methodProxy, result);
        }
        return result;
    }

    protected abstract void before(Object object, Method method, Object[] args, MethodProxy methodProxy);

    protected abstract void after(Object object, Method method, Object[] args, MethodProxy methodProxy, Object result);

    public static AbstractChainHandler chain(AbstractChainHandler... handlers) {
        if (handlers.length == 0){
            return new ProcessTimeChainHandler();
        }
        AbstractChainHandler handler = handlers[0];
        for (int i = 1; i < handlers.length; i++) {
            handler = handler.next(handlers[i]);
        }
        return handlers[0];
    }

    public AbstractChainHandler next(AbstractChainHandler next) {
        this.next = next;
        return next;
    }
}
